package middleware

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt"
)

// 设置测试环境
func TestMain(m *testing.M) {
	// 设置测试模式
	gin.SetMode(gin.TestMode)

	// 设置JWT密钥用于测试
	os.Setenv("JWT_SECRET", "test-secret-key")

	// 运行测试
	code := m.Run()

	// 清理
	os.Unsetenv("JWT_SECRET")
	os.Exit(code)
}

// 测试JWT令牌生成
func TestGenerateToken(t *testing.T) {
	tests := []struct {
		name   string
		userID string
		roles  []string
	}{
		{
			name:   "管理员令牌",
			userID: "admin",
			roles:  []string{"admin", "user"},
		},
		{
			name:   "普通用户令牌",
			userID: "user123",
			roles:  []string{"user"},
		},
		{
			name:   "多角色用户",
			userID: "moderator",
			roles:  []string{"admin", "moderator", "user"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			token, err := GenerateToken(tt.userID, tt.roles)

			if err != nil {
				t.Errorf("生成令牌不应该出错: %v", err)
			}
			if token == "" {
				t.Error("令牌不应该为空")
			}
			if len(token) <= 100 {
				t.Error("令牌长度应该合理")
			}

			// 验证令牌格式（JWT应该有三个部分，用.分隔）
			parts := strings.Split(token, ".")
			if len(parts) != 3 {
				t.Error("JWT应该有三个部分")
			}
		})
	}
}

// 测试JWT认证中间件
func TestJWTAuth(t *testing.T) {
	// 创建测试路由
	router := gin.New()
	router.Use(JWTAuth())
	router.GET("/test", func(c *gin.Context) {
		userID := c.GetString("userID")
		roles, _ := c.Get("roles")
		c.JSON(http.StatusOK, gin.H{
			"userID": userID,
			"roles":  roles,
		})
	})

	tests := []struct {
		name           string
		setupToken     func() string
		expectedStatus int
		checkResponse  func(t *testing.T, body string)
	}{
		{
			name: "有效令牌测试",
			setupToken: func() string {
				token, _ := GenerateToken("user123", []string{"admin"})
				return "Bearer " + token
			},
			expectedStatus: http.StatusOK,
			checkResponse: func(t *testing.T, body string) {
				var response map[string]interface{}
				err := json.Unmarshal([]byte(body), &response)
				if err != nil {
					t.Errorf("解析响应失败: %v", err)
				}
				if response["userID"] != "user123" {
					t.Errorf("期望userID为user123，实际为: %v", response["userID"])
				}
			},
		},
		{
			name: "缺少Authorization头",
			setupToken: func() string {
				return ""
			},
			expectedStatus: http.StatusUnauthorized,
			checkResponse: func(t *testing.T, body string) {
				if !strings.Contains(body, "missing authorization header") {
					t.Errorf("响应应该包含'missing authorization header'，实际: %s", body)
				}
			},
		},
		{
			name: "无效的Authorization格式",
			setupToken: func() string {
				return "InvalidFormat token"
			},
			expectedStatus: http.StatusUnauthorized,
			checkResponse: func(t *testing.T, body string) {
				if !strings.Contains(body, "invalid authorization format") {
					t.Errorf("响应应该包含'invalid authorization format'，实际: %s", body)
				}
			},
		},
		{
			name: "无效令牌",
			setupToken: func() string {
				return "Bearer invalid.token.here"
			},
			expectedStatus: http.StatusUnauthorized,
			checkResponse: func(t *testing.T, body string) {
				if !strings.Contains(body, "invalid token") {
					t.Errorf("响应应该包含'invalid token'，实际: %s", body)
				}
			},
		},
		{
			name: "过期令牌测试",
			setupToken: func() string {
				// 创建一个已过期的令牌
				claims := Claims{
					UserID: "testuser",
					Roles:  []string{"user"},
					StandardClaims: jwt.StandardClaims{
						ExpiresAt: time.Now().Add(-1 * time.Hour).Unix(), // 1小时前过期
						IssuedAt:  time.Now().Add(-2 * time.Hour).Unix(),
						Issuer:    "middleware-demo",
					},
				}
				token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
				tokenString, _ := token.SignedString(jwtSecret)
				return "Bearer " + tokenString
			},
			expectedStatus: http.StatusUnauthorized,
			checkResponse: func(t *testing.T, body string) {
				if !strings.Contains(body, "expired") {
					t.Errorf("响应应该包含'expired'，实际: %s", body)
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			w := httptest.NewRecorder()
			req, _ := http.NewRequest("GET", "/test", nil)

			if token := tt.setupToken(); token != "" {
				req.Header.Set("Authorization", token)
			}

			router.ServeHTTP(w, req)

			if w.Code != tt.expectedStatus {
				t.Errorf("期望状态码 %d，实际 %d", tt.expectedStatus, w.Code)
			}
			if tt.checkResponse != nil {
				tt.checkResponse(t, w.Body.String())
			}
		})
	}
}

// 测试RBAC权限中间件
func TestRequireRole(t *testing.T) {
	tests := []struct {
		name           string
		userRoles      []string
		requiredRoles  []string
		expectedStatus int
	}{
		{
			name:           "单角色匹配",
			userRoles:      []string{"admin"},
			requiredRoles:  []string{"admin"},
			expectedStatus: http.StatusOK,
		},
		{
			name:           "多角色匹配其中一个",
			userRoles:      []string{"user", "admin"},
			requiredRoles:  []string{"admin", "moderator"},
			expectedStatus: http.StatusOK,
		},
		{
			name:           "角色不匹配",
			userRoles:      []string{"user"},
			requiredRoles:  []string{"admin"},
			expectedStatus: http.StatusForbidden,
		},
		{
			name:           "多角色都不匹配",
			userRoles:      []string{"guest"},
			requiredRoles:  []string{"admin", "moderator"},
			expectedStatus: http.StatusForbidden,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			router := gin.New()
			router.Use(func(c *gin.Context) {
				// 模拟JWT中间件设置的角色信息
				c.Set("roles", tt.userRoles)
				c.Set("requestID", "test-request-id")
				c.Next()
			})
			router.Use(RequireRole(tt.requiredRoles...))
			router.GET("/test", func(c *gin.Context) {
				c.JSON(http.StatusOK, gin.H{"message": "success"})
			})

			w := httptest.NewRecorder()
			req, _ := http.NewRequest("GET", "/test", nil)
			router.ServeHTTP(w, req)

			if w.Code != tt.expectedStatus {
				t.Errorf("期望状态码 %d，实际 %d", tt.expectedStatus, w.Code)
			}
		})
	}
}

// 测试请求日志中间件
func TestRequestLogger(t *testing.T) {
	router := gin.New()
	router.Use(RequestLogger())
	router.GET("/test", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "test"})
	})
	router.POST("/auth/login", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "login"})
	})

	tests := []struct {
		name   string
		method string
		path   string
		body   string
	}{
		{
			name:   "GET请求",
			method: "GET",
			path:   "/test",
			body:   "",
		},
		{
			name:   "敏感路径POST请求",
			method: "POST",
			path:   "/auth/login",
			body:   `{"username":"test","password":"secret"}`,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			w := httptest.NewRecorder()
			var req *http.Request

			if tt.body != "" {
				req, _ = http.NewRequest(tt.method, tt.path, strings.NewReader(tt.body))
				req.Header.Set("Content-Type", "application/json")
			} else {
				req, _ = http.NewRequest(tt.method, tt.path, nil)
			}

			router.ServeHTTP(w, req)

			if w.Code != http.StatusOK {
				t.Errorf("期望状态码 %d，实际 %d", http.StatusOK, w.Code)
			}
			// 检查是否设置了请求ID头
			if w.Header().Get("X-Request-ID") == "" {
				t.Error("应该设置X-Request-ID头")
			}
		})
	}
}

// 测试Recovery中间件
func TestRecovery(t *testing.T) {
	router := gin.New()
	router.Use(Recovery())
	router.GET("/panic", func(c *gin.Context) {
		panic("测试panic")
	})
	router.GET("/normal", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "normal"})
	})

	tests := []struct {
		name           string
		path           string
		expectedStatus int
		shouldPanic    bool
	}{
		{
			name:           "正常请求",
			path:           "/normal",
			expectedStatus: http.StatusOK,
			shouldPanic:    false,
		},
		{
			name:           "Panic恢复",
			path:           "/panic",
			expectedStatus: http.StatusInternalServerError,
			shouldPanic:    true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			w := httptest.NewRecorder()
			req, _ := http.NewRequest("GET", tt.path, nil)

			router.ServeHTTP(w, req)

			if w.Code != tt.expectedStatus {
				t.Errorf("期望状态码 %d，实际 %d", tt.expectedStatus, w.Code)
			}

			if tt.shouldPanic {
				if !strings.Contains(w.Body.String(), "internal server error") {
					t.Error("响应应该包含'internal server error'")
				}
			}
		})
	}
}

// 测试敏感路径检查
func TestIsSensitivePath(t *testing.T) {
	tests := []struct {
		path     string
		expected bool
	}{
		{"/api/auth/login", true},
		{"/auth", true},
		{"/register", true},
		{"/password/reset", true},
		{"/api/users", false},
		{"/health", false},
		{"/dashboard", false},
	}

	for _, tt := range tests {
		t.Run(tt.path, func(t *testing.T) {
			result := isSensitivePath(tt.path)
			if result != tt.expected {
				t.Errorf("路径 %s: 期望 %v，实际 %v", tt.path, tt.expected, result)
			}
		})
	}
}

// 集成测试 - 测试完整的中间件链
func TestMiddlewareChain(t *testing.T) {
	router := gin.New()

	// 设置完整的中间件链
	router.Use(
		Recovery(),
		RequestLogger(),
		CORSMiddleware(),
	)

	// 公开路由
	router.POST("/login", func(c *gin.Context) {
		var req struct {
			Username string `json:"username"`
			Password string `json:"password"`
		}

		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		if req.Username == "admin" && req.Password == "admin123" {
			token, _ := GenerateToken("admin", []string{"admin"})
			c.JSON(http.StatusOK, gin.H{"token": token})
		} else {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"})
		}
	})

	// 需要认证的路由
	protected := router.Group("/api")
	protected.Use(JWTAuth(), RequireRole("admin"))
	protected.GET("/admin", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "admin area"})
	})

	t.Run("完整流程测试", func(t *testing.T) {
		// 1. 先登录获取token
		loginBody := `{"username":"admin","password":"admin123"}`
		w1 := httptest.NewRecorder()
		req1, _ := http.NewRequest("POST", "/login", strings.NewReader(loginBody))
		req1.Header.Set("Content-Type", "application/json")

		router.ServeHTTP(w1, req1)
		if w1.Code != http.StatusOK {
			t.Errorf("登录失败，状态码: %d", w1.Code)
			return
		}

		var loginResp map[string]interface{}
		err := json.Unmarshal(w1.Body.Bytes(), &loginResp)
		if err != nil {
			t.Errorf("解析登录响应失败: %v", err)
			return
		}

		token, ok := loginResp["token"].(string)
		if !ok || token == "" {
			t.Error("未能获取到有效token")
			return
		}

		// 2. 使用token访问受保护的路由
		w2 := httptest.NewRecorder()
		req2, _ := http.NewRequest("GET", "/api/admin", nil)
		req2.Header.Set("Authorization", "Bearer "+token)

		router.ServeHTTP(w2, req2)
		if w2.Code != http.StatusOK {
			t.Errorf("访问受保护路由失败，状态码: %d", w2.Code)
			return
		}
		if !strings.Contains(w2.Body.String(), "admin area") {
			t.Error("响应应该包含'admin area'")
		}
	})
}

// 基准测试
func BenchmarkJWTAuth(b *testing.B) {
	router := gin.New()
	router.Use(JWTAuth())
	router.GET("/test", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "test"})
	})

	token, _ := GenerateToken("testuser", []string{"user"})

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		w := httptest.NewRecorder()
		req, _ := http.NewRequest("GET", "/test", nil)
		req.Header.Set("Authorization", "Bearer "+token)
		router.ServeHTTP(w, req)
	}
}

func BenchmarkRequestLogger(b *testing.B) {
	router := gin.New()
	router.Use(RequestLogger())
	router.GET("/test", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "test"})
	})

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		w := httptest.NewRecorder()
		req, _ := http.NewRequest("GET", "/test", nil)
		router.ServeHTTP(w, req)
	}
}
